#pragma once

#include <fstream>
#include <string>
#include <vector>
#include "IndexedFace.h"
#include "SmoothingGroupSupport.h"
#include "IniFile.h"
#include "ObjMesh.h"
#include "MeshList.h"
#include "MaterialReader.h"
#include "VertexList.h"
#include "BoneVertex.h"
#include "ObjFace.h"

class TriangleList;
class VertexIndexSupport;

/**
* Reads and parses an obj file.
* @author Koen Samyn
*/
class ObjReader
{
public:
	/**
	* Creates a new ObjReader object for the provided file.
	* @param gameConfig the game configuration object with the information about the directories.
	* @param fileName the file to read.
	*/
	ObjReader(IniFile* gameConfig,tstring fileName);
	/**
	* Destroys the obj reader object.
	*/
	virtual ~ObjReader(void);
	/**
	* Gets a mesh with a specific name.
	* @param name the name of the mesh.
	* @return a pointer to the mesh object.
	*/
	ObjMesh* GetMesh(tstring name);
	/**
	* Gets the first mesh in this reader.
	*/
	ObjMesh* GetFirstMesh();

	/**
	* Copies all the found meshes into the mesh list.
	* @param meshList a list with the meshes.
	*/
	void CopyIntoMeshList(MeshList* meshList);
	/**
	* Instructs the reader to read the file.
	* @return true if the file was read succesfully, false otherwise.
	*/
	bool Read();
	/**
	* Gets the center point.
	*/
	DAEFloat3 GetCenterPoint();
	/**
	* Gets the height of the object 
	*/
	float GetHeight();
private:
	/**
	* The filename (.obj file)
	*/
	tstring m_FileName;
	/**
	* A material reader oject.
	*/
	MaterialReader * m_MaterialReader;
	/**
	* The game configuration object.
	*/
	IniFile* m_GameConfig;
	/**
	* The file to read
	*/
	tifstream m_Input;
	/**
	* Reads the material part for a mesh.
	* @param line the line to read.
	*/ 
	void ReadMaterialPart(tstring& line);
	/**
	* Reads the physx part for a mesh.
	* @param line the line to read.
	*/
	void ReadPhysXPart(tstring& line);
	/**
	* Reads a bone weight for a vertex.
	* @param line the line to parse.
	*/
	void ReadBoneWeights(tstring&);
	/**
	* Reads the bone definition.
	* @param line the line to parse.
	*/
	void ReadBone(tstring& line);
	/**
	* Reads the texture coordinate for a vertex.
	* @param line the line to parse.
	*/
	void ReadTexcoord(tstring&);
	/**
	* Reads the normal for a vertex.
	* @param line the line to parse.
	*/
	void ReadNormal(tstring& line);
	/**
	* Reads the position data.
	* @param line the line to parse.
	*/
	void ReadVertex(tstring&);
	/**
	* Reads the face.
	* @param line the line to parse.
	*/
	void ReadFace(tstring&);
	/**
	* Create a vertex.
	* @param vertexDef the vertex definition (as found in a line with an 'f' command.
	* @param absoluteVertexIndex the absolute vertex index.
	*/
	VertexIndexSupport* CreateVertex(vector<tstring> &vertexDef,int absoluteVertexIndex);
	/**
	* Reads a group definition.
	* @param line the line to parse.
	*/
	void ReadGroup(tstring& line);
	/**
	* Reads the skeleton definition.
	* @param line the line to parse.
	*/
	void ReadSkeleton(tstring& line);
	/**
	* Reads an animation.
	* @param line the animation to parse.
	*/
	void ReadAnimation(tstring& line);
	/**
	* Reads the smoothing group.
	* @param line the line to parse.
	*/
	void ReadSmoothingGroup(tstring&);
	/**
	* Reads the usemtl line.
	* @param line the line to parse.
	*/
	void ReadMaterial(tstring& line);
	/**
	* Create sorted vertex list.
	*/
	void CreateSortedVertexList();
	/**
	* Closes the current subgroup.
	*/
	void CloseCurrentSubgroup();
	/**
	* Closes the current mesh.
	*/
	void CloseCurrentMesh();
	/**
	* Creates tangents for the current mesh
	*/
	void CreateTangents(int vertexIndex1, int vertexIndex2, int vertexIndex3);

	vector<tstring> parseHelp;
	vector<tstring> parseHelp2;

	DAEFloat3& GetVertex(int index);
	DAEFloat3& GetNormal(int index);
	DAEFloat2& GetTexCoord(int index);
	BoneVertex& GetBoneVertex(int index);

	vector<DAEFloat3> m_vertices;
	vector<BoneVertex> m_BoneVertices;
	vector<DAEFloat2> m_texcoords;
	vector<DAEFloat3> m_normals;

	vector<ObjFace*>  m_Faces;
	vector<VertexIndexSupport*> m_IdList;
	vector<VertexIndexSupport*> m_SortedIdList;
	map<tstring,ObjMesh*>  m_MeshMap;
	vector<ObjMesh*>  m_Meshes;
	
	ObjMesh * m_CurrentMesh;
	Material * m_pCurrentMaterial;


	int m_CurrentSmoothingGroup;
	SmoothingGroup * m_SmoothingGroup;

	size_t m_VertexOffset;
	size_t m_NormalOffset;
	size_t m_TexcoordOffset;

	/**
	* the maximum vertex position.
	*/
	DAEFloat3 m_MaxVertex;
	/**
	* The minimum vertex position
	*/
	DAEFloat3 m_MinVertex;

	/**
	* The list of vertices
	*/
	VertexList* m_VertexList;
	/**
	* Index into the vertexlist for the POSITION semantic.
	*/
	int m_PositionIndexVL;
	/**
	* Index into the vertexlist for the NORMAL semantic.
	*/
	int m_NormalIndexVL;
	/**
	* Index into the vertexlist for the TEXCOORD semantic.
	*/
	int m_TexcoordIndexVL;
	/**
	* Index into the vertexlist for the TANGENT semantic.
	*/
	int m_TangentIndexVL;
	/**
	* Index into the vertexlist for the BONEWEIGHT semantic.
	*/
	int m_BoneIndexIndexVL;
	/**
	* Index into the vertexlist for the BONEWEIGHT semantic.
	*/
	int m_BoneWeightIndexVL;
	/**
	* Pointer to the current skeleton.
	*/
	Skeleton* m_pCurrentSkeleton;
	/**
	* True if a skin (and thus boneweights are defined, false otherwise
	*/
	bool m_HasSkin;
	/**
	* True if the winding for the faces should be changed, default false.
	*/
	bool m_ChangeWinding;
	/**
	* struct with the material name and the TriangleList.
	*/
	struct MaterialTriangleList{
		tstring materialName;
		TriangleList* triangleList;
	};
	/**
	* The current trianglelists.
	*/
	vector<MaterialTriangleList> m_CurrentTriangleLists;
	// -------------------------
	// Disabling default copy constructor and default assignment operator.
	// If you get a linker error from one of these functions, your class is internally trying to use them. This is
	// an error in your class, these declarations are deliberately made without implementation because they should never be used.
	// -------------------------

	ObjReader(const ObjReader& t);
	ObjReader& operator=(const ObjReader& t);
};
